//
//  BaseKnowledgeController.m
//  RAC
//
//  Created by 宜必鑫科技 on 2017/10/26.
//  Copyright © 2017年 宜必鑫科技. All rights reserved.
//

#import "BaseKnowledgeController.h"
#import "WJView.h"

@interface BaseKnowledgeController ()
@property(nonatomic,strong)RACCommand *command;
@property (nonatomic, strong) UITextField *textField;
@property (nonatomic, strong) UILabel *label;
@end

@implementation BaseKnowledgeController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //既可以让计算位置从导航栏下面开始 又可以让导航栏透明
    self.edgesForExtendedLayout = UIRectEdgeNone;
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    _textField = [[UITextField alloc]initWithFrame:CGRectMake(10, 50, ScreenWidth - 20, 50)];
    _textField.backgroundColor = [UIColor grayColor];
    [self.view addSubview:_textField];
    
    _label = [[UILabel alloc]initWithFrame:CGRectMake(10, 110, ScreenWidth - 20, 50)];
    _label.backgroundColor = [UIColor grayColor];
    [self.view addSubview:_label];
    
    //RACSignal的使用 发送信号
//    [self RACSignalMethod];
    
    // RACSubject和RACReplaySubject的简单使用  发送信号
//    [self RACSubjectMethod];
    
    // RAC数组和字典的使用
//    [self RACNSArrayAndNSdictionary];
    
    // RACCommend简单使用
//    [self RACCommendMethod];
    
    // 解决重复调用的问题
//    [self RACMulticastConnectionDemo];
    
    // 其他的用法
//    [self RACOtherMethod];
    
    [self RACHong];
}


- (void)RACSignalMethod
{
    /**
     *1.创建信号
     *2.订阅信号(订阅信号后，才会被激活)
     *3.发送信号
     
     /RACSignal底层实现
     1、创建信号，首先把didSubscribe保存到信号中，还不会触发
     2、当信号被订阅，也就是调用signal的subscribrNext：nextBlock， subscribeNext内部会创建订阅者subscriber，并把nextBlock保存到subscriber中，subscribNext内部会调用signal的didSubscribe
     3、signal的didsubcribe中调用【subscriber sendNext：@1】，sendNext底层其实就是执行subscriber的nextBlock。
     
     */
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        return [RACDisposable disposableWithBlock:^{
            //        2 发送信号
            [subscriber sendNext:@"1"];
            NSLog(@"调用这次信号");
            //      如果不再发送数据，最好调用发送信号完成方法，内部会自动调用[RACDisposable disposable]
            [subscriber sendCompleted];
            NSLog(@"销毁这次信号调用");
        }];
    }];
    
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"111");
    }];
    
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"222");
    }];
}


- (void)RACSubjectMethod
{
#pragma mark - 首先订阅信号 在 发送信号（sendNext 遍历所有的订阅者）如果写反 会收不到信号
    /**
     1、调用subscribeNext订阅信号，只是把订阅者保存起来，并且订阅者的nextBlock已经赋值了。
     2、调用sendNext发送信号，遍历刚刚保存的所有订阅者，一个一个调用订阅者的nextBlock
     */
    //    1、创建信号
    RACSubject *subject = [RACSubject subject];
    //    2、订阅信号
    [subject subscribeNext:^(id x) {
        NSLog(@"第一个订阅者：%@",x);
    }];
    [subject subscribeNext:^(id x) {
        NSLog(@"第二个订阅者:%@",x);
    }];
    //    3、发送信号
    [subject sendNext:@1];
    [subject sendNext:@2];
    
#pragma mark - 首先发送信号 在 订阅信号（遍历所有的信号）如果写反 也会收到信号 但是也是遍历的是信号
#pragma mark - 如果一个信号每被订阅一次，就需要把之前的值重复发送一遍
    /**
     1、调用sendNext发送信号，把值保存起来，然后遍历刚刚保存的所有订阅者，一个一个调用订阅者的nextBlock
     2、调用subscribeNext订阅信号，遍历所有保存的值，一个一个调用订阅者的nextBlock
     */
    //    1、创建信号
    RACReplaySubject *replySubject = [RACReplaySubject subject];
    //    2、发送信号
    [replySubject sendNext:@1];
    [replySubject sendNext:@2];
    //    3、订阅信号
    [replySubject subscribeNext:^(id  _Nullable x) {
        NSLog(@"replySubject 第一个订阅者：%@",x);
    }];
    [replySubject subscribeNext:^(id  _Nullable x) {
        NSLog(@"replySubject 第二个订阅者:%@",x);
    }];
    
}

- (void)RACNSArrayAndNSdictionary
{
    NSArray *ary = @[@1,@2,@3];
    [ary.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"数组 %@",x);
    }];
    
    
    NSDictionary *dic = @{@"name":@"wangtongke",@"age":@18};
    [dic.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
        //        遍历字典  X为RAC的元祖（RACTuple）
        //        解包元祖，会把元祖的值，按顺序给参数里边的变量赋值
        RACTupleUnpack(NSString *key,NSString *value) = x;
        NSLog(@"key = %@, value = %@",key,value);
    }];
}


- (void)RACCommendMethod
{
    //1、在RAC开发中，通常会把网络请求封装到RACCommand，直接执行某个RACCommand就能发送请求。
    //2、当RACCommand内部请求到数据的时候，需要把请求的数据传递给外界，这时候就需要通过signalBlock返回的信号传递了
    
    /* 1.创建命令 */
    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        NSLog(@"执行命令");
        /* 这里必需返回一个信号 2、创建信号，用来传递数据 */
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"请求数据"];
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"销毁信号");
            }];
        }];
    }];
    //    强引用命令，不然会自动销毁，接受不到数据
    _command = command;
    
    /* 订阅commend里面的信号 */
    [_command.executionSignals subscribeNext:^(id  _Nullable x) {
        [x subscribeNext:^(id  _Nullable x) {
            NSLog(@"executionSignals : %@",x);
        }];
    }];
    
    /* 可以直接拿到commend里面的信号 */
    [_command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"switchToLatest :%@",x);
    }];
    
    //  ******** 4、监听命令是否执行完毕，默认会来一次，可以直接跳过，skip表示跳过第一次信号
    [[command.executing skip:1] subscribeNext:^(id x) {
        if ([x boolValue] == YES){
            NSLog(@"正在执行");
        }else{
            NSLog(@"执行完成");
        }
    }];
    
    /*** 5、执行命令 ***/
    [self.command execute:@1];
}

- (void)RACMulticastConnectionDemo
{
#pragma mark -  每次订阅一次都会发送请求，这样就会导致多次请求。
    //  **********  1、创建请求信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"发送请求111111");
        [subscriber sendNext:@1];
        return nil;
    }];
    
    
    //  **********  2、订阅信号
    [signal subscribeNext:^(id x) {
        NSLog(@"接受数据11111--%@",x);
    }];
    //    订阅两次信号
    [signal subscribeNext:^(id x) {
        NSLog(@"接受数据11111---%@",x);
    }];
    
    //    会执行两次发送请求。也就是每订阅一次  就会发送一次请求
    
    
    //    RACMulicastConnection解决重复请求问题
    //  ********** 1、创建信号
    
    RACSignal *signal2  = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        NSLog(@"发送请求2");
        [subscriber sendNext:@1];
        
        return nil;
    }];
    //  ********** 2、 创建连接
    RACMulticastConnection *connect = [signal2 publish];
    
    //  **********  3、订阅信号
    //    注意：订阅信号也不能激活信号，只是保存订阅者到数据，必须通过连接，当调用连接，就会一次清调用所有订阅者的sendNext；
    [connect.signal subscribeNext:^(id x) {
        NSLog(@"订阅者一信号");
    }];
    
    [connect.signal subscribeNext:^(id x) {
        NSLog(@"订阅者二信号");
    }];
    
    // **********  4、连接
    [connect connect];
    
}

- (void)RACOtherMethod
{
    // ********************** 1、代替代理      **************************
    
    /**
     需求： 自定义redView，监听redView中按钮点击
     之前都是需要通过代理监听，给红色view添加一个代理属性，点击按钮的时候，通知代理做事情
     rac_signalForSelector:把调用某个对象的方法的信息转换成信号，只要调用这个方法，就会发送信号
     这里表示只要redView调用btnClick，就会发出信号，只需要订阅就可以了
     
     */
    WJView *view = [[WJView alloc]initWithFrame:CGRectMake(0, 200, 375, 200)];
    [self.view addSubview:view];
    
    [[view rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
    }];
    
    
    
    // ********************** 2、KVO      **************************
    [[view rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    view.center = CGPointMake(100, 100);
    
    
    
    // ********************** 3、监听事件      **************************
    UIButton *btn           = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.backgroundColor     = [UIColor purpleColor];
    btn.frame               = CGRectMake(30, 300, 200, 30);
    [btn setTitle:@"RAC" forState:UIControlStateNormal];
    [self.view addSubview:btn];
    
    [[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        NSLog(@"按钮被点击了");
    }];
    
    
    // ********************** 4、代替通知      **************************
    //    把监听到的通知，转换成信号
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
        NSLog(@"键盘弹出");
    }];
    
    
    // ********************** 5、监听文本框文字改变      **************************
    
//    [self.textField.rac_textSignal subscribeNext:^(id x) {
//        NSLog(@"文字改变了---%@",x);
//    }];
    
    
    // ********************** 6、处理多个请求，都返回结果的时候，统一做处理      **************************
    
    RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"发送请求1"];
        return [RACDisposable disposableWithBlock:^{
        }];
    }];
    RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"发送请求2"];
        });
        return [RACDisposable disposableWithBlock:^{
        }];
    }];
    
    //    使用注意：几个信号，参数一的方法就必须有几个参数，每个参数对应信号发出的数据
    [self rac_liftSelector:@selector(wtkUpdateWithDic1:withDic2:) withSignalsFromArray:@[request1,request2]];
}

- (void)wtkUpdateWithDic1:(id )dic1 withDic2:(id )dic2
{
    NSLog(@"1--%@---- 2---%@",dic1,dic2);
}


- (void)RACHong
{
    //    RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
    RAC(self.label,text) = _textField.rac_textSignal;
  
    //  RACObserve(self, name):监听某个对象的某个属性,返回的是信号
    [RACObserve(self.label, text) subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    
    //    @weakify(Obj)和@strongify(Obj),一般两个都是配套使用,在主头文件(ReactiveCocoa.h)中并没有导入，需要自己手动导入，RACEXTScope.h才可以使用。但是每次导入都非常麻烦，只需要在主头文件自己导入就好了。
    //    最新版库名 已换成  EXTScope
    //    两个配套使用，先weak再strong
    @weakify(self);
    //    @strongify(self);
    [RACObserve(self, label.text) subscribeNext:^(id x) {
        @strongify(self);
    }];
    
    
    //    RACTuplePack：把数据包装成RACTuple（元组类）
    //    把参数中的数据包装成元祖
    RACTuple *tuple = RACTuplePack(@10,@20);
    
    
    //    RACTupleUnpack：把RACTuple（元组类）解包成对应的数据。
    //    把参数再用的数据包装成元祖
    
}




















@end
